//
// Created by 98770 on 2023/6/22.
//

#include "ISPquery.h"

struct DNS_answer* resolveQueryOnISP(char *buff,int len,char *DNSserver){
    //创建socket
    static int timeoutAmount=0;
    if(timeoutAmount>5) {
        //超时次数过多，重新获取DNS服务器地址
        DNSserver= "1.1.1.1";
    }
    int sockToDNS=socket(AF_INET,SOCK_DGRAM,0);
    if(sockToDNS<0){
        printf("create socket failed!\n");
        return NULL;
    }
    //设置DNS服务器地址
    struct sockaddr_in DNSAddr;
    DNSAddr.sin_family=AF_INET;
    //端口设置为0，让系统自动分配
    DNSAddr.sin_port=htons(53);
#if _WIN32
    DNSAddr.sin_addr.S_un.S_addr=inet_addr(DNSserver);
#elif __linux__
    DNSAddr.sin_addr.s_addr=inet_addr(DNSserver);
#endif
    //bind socket
    if(sendto(sockToDNS,buff,len,0,(struct sockaddr*)&DNSAddr,sizeof(DNSAddr))<0){
        printf("sendto failed!\n");
        timeoutAmount++;
        return NULL;
    }
    //接收响应
    static char response[1024];
    int responseLen = -1;
    struct DNS_answer* answer = NULL;
#if _WIN32
    fd_set readfds;
    struct timeval timeout;
    FD_ZERO(&readfds);
    FD_SET(sockToDNS, &readfds);

    timeout.tv_sec = 1; // 超时时间为1秒
    timeout.tv_usec = 0;

    int result = select(sockToDNS + 1, &readfds, NULL, NULL, &timeout);
    if (result == -1) {
        printf("select failed!\n");
        return NULL;
    } else if (result == 0) {
        printf("recvfrom timeout!\n");
        return NULL;
    }
    int DNSAddrLen=sizeof(DNSAddr);
    responseLen=recvfrom(sockToDNS,response,sizeof(response),0,(struct sockaddr*)&DNSAddr,&DNSAddrLen);
    if(responseLen<0){
        printf("recvfrom failed!\n");
        timeoutAmount++;
        return NULL;
    }
#elif __linux__
    struct timeval timeout;
    timeout.tv_sec = 1;  // 超时时间为1秒
    timeout.tv_usec = 0;
    if (setsockopt(sockToDNS, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)) == -1) {
        perror("setsockopt failed");
        exit(EXIT_FAILURE);
    }
    socklen_t DNSAddrLen = sizeof(struct sockaddr_in);
    responseLen = recvfrom(sockToDNS, response, sizeof(response), 0, (struct sockaddr*)&DNSAddr, &DNSAddrLen);
    if(responseLen<0){
        if (errno == EWOULDBLOCK || errno == EAGAIN) {
            timeoutAmount++;
            printf("recvfrom timeout!\n");
        } else {
            timeoutAmount++;
            perror("recvfrom failed");
        }
        return NULL;
    }
#endif
    //返回响应
    answer= getAnswer(response,responseLen);
    answer->len=responseLen;
    memcpy(answer->packet,response,sizeof (response));
    close(sockToDNS);
    return answer;
}

struct DNS_answer* getAnswer(char* packet,int len){
    struct DNS_answer *answer=malloc(sizeof(struct DNS_answer));
    //跳过DNS报文头部
    strcpy(answer->domain, getDomainName(packet,len));
    char *tmp=packet+sizeof(struct dns_header);
    //跳过查询部分的NAME
    while (*tmp!=0){
        tmp+=*tmp+1;
    }
    //跳过查询部分的TYPE,class,相应部分的name
    tmp+=7;
    //解析响应部分
    answer->type=ntohs(*(unsigned short*)tmp);
    tmp+=2;
    answer->class=ntohs(*(unsigned short*)tmp);
    tmp+=2;
    answer->ttl=ntohl(*(unsigned int*)tmp);
    tmp+=4;
    //跳过data length
    tmp+=2;
    //取出ip
    strcpy(answer->ip,inet_ntoa(*(struct in_addr*)tmp));
    return answer;
}